package cn.edu.hitsz.compiler.parser;

import cn.edu.hitsz.compiler.ir.IRImmediate;
import cn.edu.hitsz.compiler.ir.IRValue;
import cn.edu.hitsz.compiler.ir.IRVariable;
import cn.edu.hitsz.compiler.ir.Instruction;
import cn.edu.hitsz.compiler.lexer.Token;
import cn.edu.hitsz.compiler.parser.table.Production;
import cn.edu.hitsz.compiler.parser.table.Status;
import cn.edu.hitsz.compiler.parser.table.symbol.ValSymbol;
import cn.edu.hitsz.compiler.symtab.SymbolTable;
import cn.edu.hitsz.compiler.utils.FileUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

// TODO: 实验三: 实现 IR 生成

/**
 * 生成IR指令序列
 */
public class IRGenerator implements ActionObserver {
    //数据结构处理有三种：1、立即数，值为本身，type为IntConst；2、终结符，table包含，值无需保存，type为本身;3、非终结符，type为终结符或null
    private SymbolTable symbolTable;
    private Stack<ValSymbol> valSymbolStack = new Stack<>();
    private List<Instruction> list = new ArrayList<>();

    @Override
    public void whenShift(Status currentStatus, Token currentToken) {
        // TODO
        //throw new NotImplementedException();
        valSymbolStack.push(new ValSymbol(currentToken));
    }

    @Override
    public void whenReduce(Status currentStatus, Production production) {
        // TODO
        //根据产生式序号进行分别处理
        switch (production.index()){
            case 6 -> {
                ValSymbol temp = new ValSymbol(production.head());
                ValSymbol def1 = valSymbolStack.pop();
                valSymbolStack.pop();
                ValSymbol def0 = valSymbolStack.pop();

                IRVariable result = IRVariable.named(def0.getType());
                IRValue operand;
                if(def1.getType().equals("IntConst")){
                    operand = IRImmediate.of(def1.getVal());
                }else {
                    operand = IRVariable.named(def1.getType());
                }
                list.add(Instruction.createMov(result,operand));
                valSymbolStack.push(temp);
            }
            case 7 -> {
                ValSymbol temp = new ValSymbol(production.head());
                ValSymbol def1 = valSymbolStack.pop();
                valSymbolStack.pop();

                IRValue operand;
                if(def1.getType().equals("IntConst")){
                    operand = IRImmediate.of(def1.getVal());
                }else {
                    operand = IRVariable.named(def1.getType());
                }
                list.add(Instruction.createRet(operand));
                valSymbolStack.push(temp);
            }
            case 8,9,11 -> {
                ValSymbol temp = new ValSymbol(production.head());
                ValSymbol def1 = valSymbolStack.pop();
                valSymbolStack.pop();
                ValSymbol def0 = valSymbolStack.pop();
                IRVariable result = IRVariable.temp();
                IRValue operand0,operand1;
                if(def1.getType().equals("IntConst")){
                    operand1 = IRImmediate.of(def1.getVal());
                }else {
                    operand1 = IRVariable.named(def1.getType());
                }
                if(def0.getType().equals("IntConst")){
                    operand0 = IRImmediate.of(def0.getVal());
                }else {
                    operand0 = IRVariable.named(def0.getType());
                }
                switch (production.index()){
                    case 8 -> list.add(Instruction.createAdd(result,operand0,operand1));
                    case 9 -> list.add(Instruction.createSub(result,operand0,operand1));
                    case 11 -> list.add(Instruction.createMul(result,operand0,operand1));
                }
                temp.setType(result.getName());
                valSymbolStack.push(temp);
            }
            case 10,12,14,15 -> {
                ValSymbol temp = new ValSymbol(production.head());
                ValSymbol def = valSymbolStack.pop();
                temp.setVal(def.getVal());
                temp.setType(def.getType());
                valSymbolStack.push(temp);
            }
            case 13 ->{
                valSymbolStack.pop();
                ValSymbol def = valSymbolStack.pop();
                valSymbolStack.pop();
                ValSymbol temp = new ValSymbol(production.head());
                temp.setVal(def.getVal());
                temp.setType(def.getType());
                valSymbolStack.push(temp);
            }
        }
    }


    @Override
    public void whenAccept(Status currentStatus) {
        // TODO
        //throw new NotImplementedException();
    }

    @Override
    public void setSymbolTable(SymbolTable table) {
        // TODO
        //throw new NotImplementedException();
        this.symbolTable = table;
    }

    public List<Instruction> getIR() {
        // TODO
        //throw new NotImplementedException();
        return this.list;
    }

    public void dumpIR(String path) {
        FileUtils.writeLines(path, getIR().stream().map(Instruction::toString).toList());
    }
}

